{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多线程的优势"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 并发(concurrent): 在单核CPU上，多个线程通过快速切换占用CPU时间片，从宏观上看像是同时执行，但实际是交替执行。\n",
    "- 并行(parallel): 在多核或多CPU环境下，多个线程真正同时执行，每个线程独立占用一个CPU核心。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 线程的消耗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 上下文切换开销:\n",
    "  - 线程越多，CPU在切换线程上下文时花费的额外时间越多\n",
    "  - 极端情况下，频繁切换的开销可能超过单线程持续计算的效率\n",
    "- 创建销毁成本:\n",
    "  - 操作系统创建和销毁线程是\"重\"操作（高耗时、高资源消耗）\n",
    "  - 实时创建/销毁线程会显著降低系统实时性能\n",
    "- 负载尖峰风险:\n",
    "  - 大量线程同时唤醒会导致系统负载呈锯齿状波动\n",
    "  - 可能引发瞬间负载过大导致系统宕机\n",
    "- 数量限制原则:\n",
    "  - 不是线程越多越好，需要根据硬件核心数和任务类型合理设置\n",
    "  - 服务器程序通常运行在多核环境，但线程数仍需优化控制\n",
    "- 底层实现:\n",
    "  - 无论使用pthread库还是C++11线程库，最终都需通过操作系统内核完成线程创建\n",
    "  - Linux系统线程创建涉及内核态切换和资源分配，性能消耗较大"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 线程的消耗的4点详解"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 线程的创建和销毁"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程创建与销毁的重量级操作\n",
    "\n",
    "- 空间切换开销：用户态创建线程需切换到内核空间，完成后再切换回用户空间，涉及两次空间切换\n",
    "- 内核数据结构创建：内核需要为线程创建PCB(`task_struct`)、内核栈、页目录/页表等数据结构\n",
    "- 地址空间管理：通过`vm_area_struct`描述代码段/数据段的内存属性（可读/可写/可执行等）\n",
    "- 共享机制：线程共享进程地址空间，但相关数据结构仍需独立创建和初始化"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "plaintext"
    }
   },
   "source": [
    "线程创建与销毁对系统资源的影响\n",
    "\n",
    "- 性能消耗：单次线程创建/销毁就涉及大量内核操作，实时创建会分散系统处理业务请求的资源\n",
    "- 业务场景影响：在高并发场景（如秒杀服务）中，频繁创建线程会严重影响系统处理能力\n",
    "- 类比连接池：类似数据库连接池原理，线程池避免实时创建/销毁带来的性能损耗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程池的主要原因之一 \n",
    "\n",
    "- 预创建优势：在业务高峰前预先创建好线程，避免在请求处理过程中实时创建\n",
    "- 资源聚焦：将系统资源集中在业务处理而非线程管理上，提高整体吞吐量\n",
    "- 性能对比：测试表明线程池可显著减少线程创建/销毁的时间开销（具体数值待补充）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程栈占用大量内存\n",
    "\n",
    "- 默认栈大小：Linux下默认线程栈大小为8MB（通过`ulimit -a`可查看）\n",
    "- 空间占用计算：32位系统3GB用户空间/8MB栈大小 → 理论最多384个线程\n",
    "- 实际限制：因需预留代码段/数据段/共享库空间，实际线程数约380个"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time(seconds)        unlimited\n",
      "file(blocks)         unlimited\n",
      "data(kbytes)         unlimited\n",
      "stack(kbytes)        8192\n",
      "coredump(blocks)     0\n",
      "memory(kbytes)       unlimited\n",
      "locked memory(kbytes) 65536\n",
      "process              62299\n",
      "nofiles              1048576\n",
      "vmemory(kbytes)      unlimited\n",
      "locks                unlimited\n",
      "rtprio               0\n"
     ]
    }
   ],
   "source": [
    "! ulimit -a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 线程栈本身占用大量内存"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "用户空间与线程栈的关系\n",
    "- 32位系统划分：4GB地址空间中用户空间占3GB（$3\\times 1024 = 3072$ MB）\n",
    "- 共享特性：同一进程的所有线程共享进程地址空间，但每个线程有独立栈空间"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程栈的默认大小与限制\n",
    "- 标准配置：默认8MB栈空间（可通过`pthread_attr_setstacksize`调整）\n",
    "- 计算公式：最大线程数 = 用户空间总量(3072MB) / 单个线程栈大小(8MB) = 384"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Linux下线程创建数量的上限\n",
    "- 实测结果：(32位 4G)实际创建线程数约380个（因需保留其他内存区域）\n",
    "- 资源耗尽表现：超过限制会导致\"cannot allocate memory\"错误"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程栈占用内存的原因分析\n",
    "\n",
    "- 执行必需：每个线程都需要独立栈空间执行线程函数\n",
    "- 资源竞争：大量线程会耗尽用户空间内存，导致无法正常执行业务逻辑\n",
    "- 极端案例：创建2000个线程（64位系统）可能占用系统一半内存"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "64位系统下线程创建数量的变化\n",
    "\n",
    "- 空间扩大：64位系统用户空间显著增大，可创建更多线程\n",
    "- 新问题：虽然数量限制放宽，但大量线程仍会导致内存压力"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 假设物理内存 16 GB，默认线程栈 8 MB，则理论上最多约 `16 GB / 8 MB ≈ 2048` 个线程。若将栈缩小到 1 MB，则可提升到约 16,000 个。不过栈太小可能导致栈溢出，需要根据应用情况调整。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "正在编译 thread_limit_probe.cpp ...\n",
      "运行 thread_limit_probe ...\n",
      "线程创建探测程序启动 (PID 36544)\n",
      "\n",
      "==== 线程创建最终统计 ====\n",
      "成功创建线程总数 : 32744\n",
      "终止原因           : 创建线程失败\n",
      "错误信息           : Resource temporarily unavailable\n",
      "错误码 (errno)     : 11\n",
      "线程栈软限制 (字节) : 8388608\n",
      "\n",
      "进程资源快照:\n",
      "  VmSize:\t268386904 kB\n",
      "  VmRSS:\t  275200 kB\n",
      "  Threads:\t32745\n",
      "===========================\n",
      "所有线程已回收，程序结束。\n",
      "等待 5 秒后删除可执行文件 ...\n",
      "删除 thread_limit_probe ...\n",
      "完成。\n"
     ]
    }
   ],
   "source": [
    "! cd code && bash thread_limit_probe.sh"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程数量过多带来的问题\n",
    "- 内存耗尽：线程栈占用过多内存导致业务处理缺乏可用内存\n",
    "- 调度开销：大量线程增加上下文切换成本（未展开讲解具体机制）\n",
    "- 系统稳定性：可能引发OOM(Out Of Memory)导致进程被终止"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程栈大小的优化\n",
    "\n",
    "- 调整策略：根据实际需求减小栈大小（如从8MB降至8KB）\n",
    "- C++11支持：标准库提供线程属性设置接口，可灵活控制栈大小\n",
    "- 平衡原则：在栈深度需求和线程数量间取得平衡，避免栈溢出"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 线程过多"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上下文切换耗时\n",
    "\n",
    "- 切换代价：线程调度需要进行上下文切换，保存当前线程状态并恢复下一个线程状态，这个过程会消耗大量CPU时间\n",
    "- 实例说明：就像多人共用计算器时，频繁交接计算器需要讨论保存和恢复数据，耗费额外时间"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程过多对CPU利用率的影响\n",
    "\n",
    "- 效率降低：当50%CPU时间用于上下文切换时，实际业务处理时间只剩50%，导致CPU利用率显著下降\n",
    "- 资源浪费：线程过多会使CPU将更多时间花费在调度而非实际业务处理上"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程调度与上下文切换的实例解释\n",
    "\n",
    "- 类比说明：300人协作时，沟通协调时间远大于实际工作时间，类似线程过多导致调度开销过大\n",
    "- 关键数据：创建几千个线程时，上下文切换可能消耗超过50%的CPU时间"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程类型与多线程程序设计的考量\n",
    "- IO密集型：适合多线程，因IO操作会阻塞线程，多线程可提高CPU利用率\n",
    "- CPU密集型：线程数应与CPU核心数匹配，过多线程会导致频繁上下文切换\n",
    "- 混合型任务：可针对不同任务类型设计不同线程数量策略"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线程过多可能导致的系统宕机\n",
    "- 唤醒风暴：大量等待IO的线程同时被唤醒，导致系统负载瞬间飙升\n",
    "- 实例说明：大型3D游戏从最小化恢复时，大量线程同时唤醒可能导致系统卡死或崩溃\n",
    "- 内存压力：线程栈内存占用过多，可能触发大量内存交换操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 合理的线程数量建议"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 核心数匹配：通常建议线程数与CPU物理核心数相同（如4核CPU用4线程）\n",
    "- IO密集型调整：对于IO密集型任务，可适当增加线程数（如核心数的2-3倍）\n",
    "- 开源实践：主流网络库（如moduo、libevent、Netty）均采用核心数确定线程数的策略"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "codemirror_mode": "text/x-c++src",
   "file_extension": ".cpp",
   "mimetype": "text/x-c++src",
   "name": "c++",
   "version": "17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
